﻿
namespace Patterns4Net.ArchitectureExplorer.ViewModels.Services
{
    using System;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Windows.Controls;
    using System.Windows.Markup;
    using System.Xml;
    using System.Xml.Linq;
    using System.Xml.Xsl;
    using Caliburn.Micro;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Messages;

    [Export(typeof(DocumentationParser))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class DocumentationParser
    {
        private readonly ILog logger;

        private readonly IEventAggregator eventAggregator;

        private XslCompiledTransform transform;

        private object transformLock = new object();

        [ImportingConstructor]
        public DocumentationParser(ILog logger, IEventAggregator eventAggregator)
        {
            this.logger = logger;
            this.eventAggregator = eventAggregator;
        }

        public TextBlock Transform(XElement element)
        {
            this.LoadTransform();
            var stream = new MemoryStream();

            // transform xml into xaml
            try
            {
                using (var writer = new XmlTextWriter(stream, Encoding.UTF8))
                {
                    var document = new XDocument(element);
                    var reader = new XmlTextReader(new StringReader(document.ToString()));
                    this.transform.Transform(reader, writer);
                }
            }
            catch (Exception ex)
            {
                this.logger.Error(ex);
                this.eventAggregator.Publish(
                    new ErrorMessage(
                        "Documentation in an xml format cannot be transformed in xaml. " +
                        "Detailed exception information has been logged."));
            }

            // parse xaml
            try
            {
                var xaml = Encoding.UTF8.GetString(stream.GetBuffer()).Trim();
                // xslt processor generates trange characters at the beginning and at the end, 
                // so we trim them...
                xaml = xaml.Substring(xaml.IndexOf("<"));
                xaml = xaml.Substring(0, xaml.LastIndexOf(">") + 1);
                var result = XamlReader.Parse(xaml);
                if (result is TextBlock)
                {
                    return (TextBlock)result;
                }
            }
            catch (Exception ex)
            {
                this.logger.Error(ex);
                this.eventAggregator.Publish(
                    new ErrorMessage(
                        "Xaml content, which is a result of the transformation from an xml format, cannot be parsed. " +
                        "Detailed exception information has been logged."));
            }

            return null;
        }

        public void LoadTransform()
        {
            if (this.transform != null)
            {
                return;
            }

            lock (this.transformLock)
            {
                if (this.transform != null)
                {
                    return;
                }

                try
                {
                    this.transform = new XslCompiledTransform();
                    this.transform.Load(
                        "Patterns4Net.ArchitectureExplorer.Config.documentation.xslt",
                        XsltSettings.TrustedXslt,
                        new EmbeddedResourceResolver());
                }
                catch (Exception ex)
                {
                    this.logger.Error(ex);
                    this.eventAggregator.Publish(
                        new ErrorMessage(
                            "Template for transformation of documentation in an xml format cannot be loaded from resources. " +
                            "Detailed exception information was logged."));
                }
            }
        }


        /// <summary>
        /// Enables loading of xslt stylesheets from embedded resources.
        /// </summary>
        public class EmbeddedResourceResolver : XmlUrlResolver
        {
            public override object GetEntity(
                Uri absoluteUri,
                string role,
                Type ofObjectToReturn)
            {
                var assembly = Assembly.GetExecutingAssembly();
                return assembly.GetManifestResourceStream(absoluteUri.Segments.Last());
            }
        }
    }
}
